const jwt = require('jsonwebtoken')
const clients = require('@/websocket')
const { decrypt, getSessionuserId, encrypt, dateFormat } = require('@/utils/common.util')
const trash = require('@/utils/ini.unit')
const { apiLoginMiniWeb, apiGetIPAddress } = require('@/http')
const { getAsync, delAsync } = require('@/redis')
const JsonResult = require('@/utils/httpResponse.unit')
const UserModel = require('@/models/user.model')
const SitesModel = require('@/models/sites.model')
const ArticleModel = require('@/models/article.model')
const MessageModel = require('@/models/message.model')
const UserAgreeCommentModel = require('@/models/user_agree_comment.model')
const getAccessToken = require('@/utils/generateAccessToken')
const DynamicsModel = require('@/models/dynamics.model')
const {
  apiCreateQRCode,
  apiGetGiteeAccesstokenByCode,
  apiGetGiteeUserInfo,
  apiGetGithubAccesstokenByCode,
  apiGetGithubUserInfo
} = require('@/http')
const UserFocusUserModel = require('@/models/user_focus_user.model')
const ArticleCommentModel = require('@/models/article_comment.model')
const ShareCircleModel = require('@/models/share_circle.model')
const UserCollectToolModel = require('@/models/user_collect_tool.model')
const ShareCircleCommentModel = require('@/models/sharecircle_comment.model')
const UserCollectArticleModel = require('@/models/user_collect_article.model')
const UserAgreeArticleModel = require('@/models/user_agree_article.model')
const LoginLogController = require('@/models/login_log.model')
const signInModel = require('@/models/sign_in.model')

class UserController {
  /**
   * search users page
   * @param {*} req
   * @param {*} response
   */
  async getAllUsers (req, response) {
    try {
      const data = await UserModel.find(req.query, true)
      data.list = data.list.map(user => {
        return user
      })
      JsonResult.success({
        req,
        response,
        data,
        message: '查询用户列表成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询用户列表失败' })
    }
  }

  /**
   * get userInfo by id
   * @param {*} req
   * @param {*} response
   */
  async getUserById (req, response) {
    try {
      const data = await UserModel.findOne(req.query, true)
      JsonResult.success({
        req,
        response,
        data: {
          ...data,
          openid: null,
          bindWx: Boolean(data.openid),
          bindEmail: Boolean(data.email),
          username: null
        },
        message: '查询用户信息成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询用户信息失败' })
    }
  }

  /**
   * get current login userInfo
   * @param {*} req
   * @param {*} response
   */
  async getSessionUserInfo (req, response) {
    const userInfo = req.sessionuser
    const userId = userInfo.id
    const signTime = dateFormat(new Date(), 'YYYY-mm-dd')
    try {
      const exist = await signInModel.exist(userId, signTime)
      JsonResult.success({
        req,
        response,
        data: {
          ...req.sessionuser,
          hasSigned: Boolean(exist)
        },
        message: '查询用户信息成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询用户信息失败' })
    }
  }

  /**
   * delete user by id
   * @param {*} req
   * @param {*} response
   */
  async deleteById (req, response) {
    try {
      const { id } = req.query
      await UserModel.delete(id)
      JsonResult.success({
        req,
        response,
        message: '删除用户成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '删除用户失败' })
    }
  }

  /**
   * user register
   * @param {*} req
   * @param {*} response
   */
  async createUser (req, response) {
    try {
      const { username, password, confirmPassword, code } = req.body
      const captcha = req.session.captcha
      if (!captcha || (code.toLowerCase() !== captcha.toLowerCase())) {
        return JsonResult.fail({ req, response, message: '验证码错误' })
      }
      const { aesKey, aesIv } = req.session.publicSecretKeys
      const userdepassword = decrypt(password, aesKey, aesIv)
      const userdeConfirmpassword = decrypt(confirmPassword, aesKey, aesIv)
      if (userdepassword !== userdeConfirmpassword) {
        return JsonResult.fail({ req, response, message: '两次密码不一致' })
      }
      const userId = await UserModel.create({
        username: username,
        password: encrypt(userdepassword, trash.aesKey, trash.aesIIv)
      })
      this.sendSystemRegisterMessage(userId)
      JsonResult.success({
        req,
        response,
        message: '用户注册成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '用户注册失败' })
    }
  }

  /**
   * update userInfo by id
   * @param {*} req
   * @param {*} response
   */
  async updateUser (req, response) {
    try {
      const { id, hasUpdateBirthday } = req.sessionuser
      const { nickname, job, avatar, description, gender, blogAddress, birthday, startWorkTime } = req.body
      await UserModel.update({
        nickname,
        job,
        avatar,
        description,
        gender,
        blogAddress,
        birthday: hasUpdateBirthday ? null : birthday, // 已经修改过出生日期的不可再次更新
        startWorkTime,
        id
      })
      JsonResult.success({
        req,
        response,
        message: '编辑用户资料成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '编辑用户资料失败' })
    }
  }

  /**
   * 修改用户私信限制设置
   * @param {*} req
   * @param {*} response
   */
  async changeImRestrict (req, response) {
    const imRestrict = req.body.imRestrict
    const userId = req.sessionuser.id
    try {
      await UserModel.update({ imRestrict, id: userId })
      JsonResult.success({
        req,
        response,
        message: '更新私信设置成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '更新私信设置失败' })
    }
  }

  /**
   * user bind email
   * @param {*} req
   * @param {*} response
   * @returns
   */
  async bindEmail (req, response) {
    try {
      const id = req.sessionuser.id
      const { email, code } = req.body
      const exist = await UserModel.findOne({ email })
      if (exist) {
        return JsonResult.fail({ req, response, message: '邮箱已经被注册' })
      }
      const sessionCode = await getAsync(email)
      if (code !== sessionCode) {
        return JsonResult.fail({ req, response, message: '验证码不正确' })
      }
      await UserModel.update({ email, id })
      delAsync(email)
      req.session.sendEmailCD = false
      JsonResult.success({
        req,
        response,
        message: '绑定邮箱成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '邮箱绑定失败' })
    }
  }

  /**
   * user login
   * @param {*} req
   * @param {*} response
   * @returns
   */
  async userLogin (req, response) {
    const { username, password, code, email, emailLogin } = req.body
    // 邮箱验证码登录注册
    if (emailLogin) {
      return this.emailLoginOrRegister(req, response, email, code)
    }
    // 用户名/邮箱 密码登录
    this.passwordEnterLogin(req, response, username, password, code)
  }

  async emailLoginOrRegister (req, response, email, code) {
    try {
      const sessionCode = await getAsync(email)
      if (code !== sessionCode) {
        return JsonResult.fail({ req, response, message: '验证码不正确' })
      }
      const user = await UserModel.findOne({ email }, false)
      if (!user) {
        const userId = await UserModel.create({ email })
        await this.loginResponseResult(req, response, userId, '2')
        delAsync(email)
        return this.sendSystemRegisterMessage(userId)
      }
      delAsync(email)
      req.session.sendEmailCD = false
      await this.loginResponseResult(req, response, user.id, '2')
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '用户登录失败' })
    }
  }

  async passwordEnterLogin (req, response, username, password, code) {
    try {
      const captcha = req.session.captcha
      if (!captcha || (code.toLowerCase() !== captcha.toLowerCase())) {
        return JsonResult.fail({ req, response, message: '验证码错误' })
      }
      const { aesKey, aesIv } = req.session.publicSecretKeys
      const decodePassword = decrypt(password, aesKey, aesIv)
      const encryptNewPassword = encrypt(decodePassword, trash.aesKey, trash.aesIIv)
      const REG_EMAIL = /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(\.[a-zA-Z0-9_-])+/
      const isEmail = REG_EMAIL.test(username)
      const sqlQuery = isEmail ? { email: username } : { username }
      const user = await UserModel.findOne(sqlQuery, false)
      if (!user) {
        return JsonResult.fail({ req, response, message: '用户名不存在' })
      }
      if (user.password !== encryptNewPassword) {
        return JsonResult.fail({ req, response, message: '密码错误' })
      }
      await this.loginResponseResult(req, response, user.id, '1')
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '用户登录失败' })
    }
  }

  /**
   * change user password
   * @param {*} req
   * @param {*} response
   */
  async userChangePassword (req, response) {
    try {
      const sessionId = await getSessionuserId(req)
      const { type, newpassword, oldpassword, code, email, confirmPassword } = req.body
      const { aesKey, aesIv } = req.session.publicSecretKeys
      const userdepassword = decrypt(newpassword, aesKey, aesIv)
      const userdeConfirmPassword = decrypt(confirmPassword, aesKey, aesIv)
      if (userdepassword !== userdeConfirmPassword) {
        return JsonResult.fail({ req, response, message: '两次输入的密码不一致' })
      }
      // 邮箱验证码修改
      if (type === '0') {
        const sessionCode = await getAsync(email)
        const user = await UserModel.findOne({ email, id: sessionId })
        if (!user) {
          return JsonResult.fail({ req, response, message: '邮箱不正确' })
        }
        if (sessionCode !== code) {
          return JsonResult.fail({ req, response, message: '邮箱验证码不正确' })
        }
      } else if (type === '1') {
        // 原密码修改
        const enoldpassword = encrypt(decrypt(oldpassword, aesKey, aesIv), trash.aesKey, trash.aesIIv)
        const user = await UserModel.findOne({ id: sessionId, password: enoldpassword })
        if (!user) {
          return JsonResult.fail({ req, response, message: '原密码不正确' })
        }
      } else if (type === '2') {
        // todo 设置密码
      } else {
        return JsonResult.fail({ req, response, message: 'type异常，修改密码失败' })
      }
      const password = encrypt(userdepassword, trash.aesKey, trash.aesIIv)
      await UserModel.updatePassword(password, sessionId)
      req.session.sendEmailCD = false
      JsonResult.success({
        req,
        response,
        message: '修改密码成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '修改密码失败' })
    }
  }

  /**
   * user collect article
   * @param {*} req
   * @param {*} response
   */
  async userCollectArticle (req, response) {
    try {
      const userId = req.sessionuser.id
      const { articleId, uid } = req.query
      const exist = await UserCollectArticleModel.findOne(userId, articleId)
      const article = await ArticleModel.exists({ id: articleId })
      if (!article) {
        return JsonResult.fail({ req, response, message: '文章不存在' })
      }
      if (exist) {
        return JsonResult.fail({ req, response, message: '用户已收藏' })
      }
      await UserCollectArticleModel.add(userId, articleId)
      await ArticleModel.autoIncre(articleId, 'collectCounts')
      if (article.userId !== userId) {
        DynamicsModel.add({ userId, type: '4', articleId })
        const [sourceUserId, targetUserId, itemType] = [userId, uid, '2']
        const params = { sourceUserId, targetUserId, articleId, itemType }
        MessageModel.exists(params).then(exists => {
          if (!exists) {
            try {
              MessageModel.add(params)
            } catch (error) {
              console.log(error, '生成消息失败')
            }
          }
        })
      }
      JsonResult.success({
        req,
        response,
        message: '收藏成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '收藏文章失败' })
    }
  }

  /**
   * user uncollect article
   * @param {*} req
   * @param {*} response
   */
  async userUnCollectArticle (req, response) {
    try {
      const userId = req.sessionuser.id
      const articleId = req.query.articleId
      const exist = await UserCollectArticleModel.findOne(userId, articleId)
      const article = await ArticleModel.exists({ id: articleId })
      if (!article) {
        return JsonResult.fail({ req, response, message: '文章不存在' })
      }
      if (!exist) {
        return JsonResult.fail({ req, response, message: '还未收藏' })
      }
      await UserCollectArticleModel.delete({ userId, articleId })
      await ArticleModel.autoDec(articleId, 'collectCounts')
      DynamicsModel.delete({ userId, type: '4', articleId })
      JsonResult.success({
        req,
        response,
        message: '取消收藏成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '取消收藏失败' })
    }
  }

  /**
   * user collect tool
   * @param {*} req
   * @param {*} response
   */
  async userCollectTool (req, response) {
    try {
      const userId = req.sessionuser.id
      const toolId = req.query.toolId
      const exist = await UserCollectToolModel.findOne(userId, toolId)
      const existTool = await SitesModel.findOneTool(toolId)
      if (!existTool) {
        return JsonResult.fail({ req, response, message: '工具不存在' })
      }
      if (exist) {
        return JsonResult.fail({ req, response, message: '用户已收藏' })
      }
      await UserCollectToolModel.add(userId, toolId)
      DynamicsModel.add({ userId, type: '4', toolId })
      JsonResult.success({
        req,
        response,
        message: '收藏成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '收藏失败' })
    }
  }

  /**
   * user uncollect tool
   * @param {*} req
   * @param {*} response
   */
  async userUnCollectTool (req, response) {
    try {
      const userId = req.sessionuser.id
      const toolId = req.query.toolId
      const exist = await UserCollectToolModel.findOne(userId, toolId)
      const existTool = await SitesModel.findOneTool(toolId)
      if (!existTool) {
        return JsonResult.fail({ req, response, message: '工具不存在' })
      }
      if (!exist) {
        return JsonResult.fail({ req, response, message: '用户还未收藏' })
      }
      await UserCollectToolModel.delete({ userId, toolId })
      DynamicsModel.delete({ userId, toolId })
      JsonResult.success({
        req,
        response,
        message: '取消收藏成功'
      })
    } catch (error) {
      JsonResult.success({
        req,
        response,
        message: '取消收藏失败'
      })
    }
  }

  /**
   * user agree article
   * @param {*} req
   * @param {*} response
   */
  async userAgreeArticle (req, response) {
    try {
      const userId = req.sessionuser.id
      const { articleId, uid } = req.query
      const exist = await UserAgreeArticleModel.findOne(userId, articleId)
      const article = await ArticleModel.exists({ id: articleId })
      if (!article) {
        return JsonResult.fail({ req, response, message: '文章不存在' })
      }
      if (exist) {
        return JsonResult.fail({ req, response, message: '重复点赞' })
      }
      await UserAgreeArticleModel.add(userId, articleId)
      ArticleModel.autoIncre(articleId, 'likeCounts')
      if (article.userId !== userId) {
        DynamicsModel.add({ userId, type: '2', articleId })
        const [sourceUserId, targetUserId, itemType] = [userId, uid, '1']
        const params = { sourceUserId, targetUserId, articleId, itemType }
        MessageModel.exists(params).then(exists => {
          if (!exists) {
            try {
              MessageModel.add(params)
            } catch (error) {
              console.log(error, '生成消息失败')
            }
          }
        })
      }
      JsonResult.success({
        req,
        response,
        message: '点赞成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '点赞失败' })
    }
  }

  /**
   * user unagree article
   * @param {*} req
   * @param {*} response
   */
  async userUnAgreeArticle (req, response) {
    try {
      const userId = req.sessionuser.id
      const articleId = req.query.articleId
      const exist = await UserAgreeArticleModel.findOne(userId, articleId)
      const article = await ArticleModel.exists({ id: articleId })
      if (!article) {
        return JsonResult.fail({ req, response, message: '文章不存在' })
      }
      if (!exist) {
        return JsonResult.fail({ req, response, message: '还未点赞' })
      }
      await UserAgreeArticleModel.delete({ userId, articleId })
      ArticleModel.autoDec(articleId, 'likeCounts')
      DynamicsModel.delete({ userId, type: '2', articleId })
      JsonResult.success({
        req,
        response,
        message: '取消点赞成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '取消点赞失败' })
    }
  }

  /**
   * user focus user
   * @param {*} req
   * @param {*} response
   */
  async userFocusUser (req, response) {
    try {
      const userId = req.sessionuser.id
      const focusId = req.query.focusId
      const exist = await UserFocusUserModel.findOne(userId, focusId)
      const user = await UserModel.findOne({ id: focusId }, false)
      if (!user) {
        return JsonResult.fail({ req, response, message: '关注的用户不存在' })
      }
      if (userId === focusId) {
        return JsonResult.fail({ req, response, message: '不能关注自己' })
      }
      if (exist) {
        return JsonResult.fail({ req, response, message: '已关注' })
      }
      await UserFocusUserModel.add(userId, focusId)
      DynamicsModel.add({ userId, type: '3', focusUserId: focusId })
      const [sourceUserId, targetUserId, itemType] = [userId, focusId, '3']
      const params = { sourceUserId, targetUserId, itemType }
      MessageModel.exists(params).then(exists => {
        if (!exists) {
          try {
            MessageModel.add(params)
          } catch (error) {
            console.log(error, '生成消息失败')
          }
        }
      })
      JsonResult.success({
        req,
        response,
        message: '关注成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '关注失败' })
    }
  }

  /**
   * user unfocus user
   * @param {*} req
   * @param {*} response
   */
  async userUnFocusUser (req, response) {
    try {
      const userId = req.sessionuser.id
      const focusId = req.query.focusId
      const exist = await UserFocusUserModel.findOne(userId, focusId)
      const user = await UserModel.findOne({ id: focusId }, false)
      if (!user) {
        return JsonResult.fail({ req, response, message: '用户不存在' })
      }
      if (!exist) {
        return JsonResult.fail({ req, response, message: '还未关注' })
      }
      await UserFocusUserModel.delete({ userId, focusId })
      DynamicsModel.delete({ userId, type: '3', focusUserId: focusId })
      JsonResult.success({
        req,
        response,
        message: '取消关注成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '取消关注失败' })
    }
  }

  /**
   * find user is focus user
   * @param {*} req
   * @param {*} response
   */
  async userIsFocus (req, response) {
    try {
      const { focusId } = req.query
      const userId = await getSessionuserId(req)
      const data = await UserFocusUserModel.findOne(userId, focusId)
      JsonResult.success({
        req,
        response,
        data: Boolean(data),
        message: '查询成功'
      })
    } catch (error) {
      JsonResult.success({
        req,
        response,
        data: false,
        message: '查询成功'
      })
    }
  }

  /**
   * get user focus users and foucused user
   * @param {*} req
   * @param {*} response
   */
  async findFocusUsers (req, response) {
    try {
      const sessionId = await getSessionuserId(req)
      // 关注的用户列表
      const focusdata = await UserFocusUserModel.find(req.query.userId, sessionId, 'userId')
      // 关注者的用户列表
      const focuseddata = await UserFocusUserModel.find(req.query.userId, sessionId, 'focusId')
      JsonResult.success({
        req,
        response,
        data: { focusdata, focuseddata },
        message: '查询成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询失败' })
    }
  }

  /**
   * statistics user counts
   * @param {*} req
   * @param {*} response
   */
  async statisticsCounts (req, response) {
    try {
      const { userId } = req.query
      const focusdata = await UserFocusUserModel.findFocusCount(userId)
      const collectCounts = await UserCollectArticleModel.total({ userId })
      const shareCount = await ShareCircleModel.total(userId)
      JsonResult.success({
        req,
        response,
        data: {
          ...focusdata,
          collectCounts,
          shareCount
        },
        message: '查询成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询失败' })
    }
  }

  /**
   * user agree comment
   * @param {*} req
   * @param {*} response
   */
  async agreeComment (req, response) {
    try {
      const userId = req.sessionuser.id
      const { commentId, itemType } = req.query
      let comment = null
      if (itemType === '1') {
        comment = await ArticleCommentModel.findOne({ id: commentId })
      } else if (itemType === '2') {
        comment = await ShareCircleCommentModel.findOne({ id: commentId })
      }
      if (!comment) {
        return JsonResult.fail({ req, response, message: '评论不存在' })
      }
      const exist = await UserAgreeCommentModel.findOne(userId, commentId)
      if (exist) {
        return JsonResult.fail({ req, response, message: '重复点赞' })
      }
      await UserAgreeCommentModel.add(userId, commentId, itemType)
      if (itemType === '1') {
        ArticleCommentModel.autoIncre(commentId, 'likeCounts')
      }
      JsonResult.success({
        req,
        response,
        message: '点赞成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '点赞失败' })
    }
  }

  /**
     * user agree comment
     * @param {*} req
     * @param {*} response
     */
  async unagreeComment (req, response) {
    try {
      const userId = req.sessionuser.id
      const { commentId, itemType } = req.query
      let comment = null
      if (itemType === '1') {
        comment = await ArticleCommentModel.findOne({ id: commentId })
      } else if (itemType === '2') {
        comment = await ShareCircleCommentModel.findOne({ id: commentId })
      }
      if (!comment) {
        return JsonResult.fail({ req, response, message: '评论不存在' })
      }
      const exist = await UserAgreeCommentModel.findOne(userId, commentId)
      if (!exist) {
        return JsonResult.fail({ req, response, message: '还未点赞' })
      }
      await UserAgreeCommentModel.delete({ userId, commentId })
      if (itemType === '1') {
        ArticleCommentModel.autoDec(commentId, 'likeCounts')
      }
      JsonResult.success({
        req,
        response,
        message: '取消点赞成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '取消点赞失败' })
    }
  }

  /**
   * get user collect tools
   * @param {*} req
   * @param {*} response
   * @returns
   */
  async userGetCollectTool (req, response) {
    try {
      const userId = req.query.userId
      if (!userId) {
        return JsonResult.fail({ req, response, message: '缺少userId' })
      }
      const data = await UserCollectToolModel.find(userId)
      JsonResult.success({
        req,
        response,
        message: '查询成功',
        data
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '查询失败' })
    }
  }

  /**
   * generate user bind wx qrcode
   * @param {*} req
   * @param {*} response
   */
  async generateBindQRCode (req, response) {
    try {
      const sessionId = req.sessionuser.id
      const authId = req.query.authId
      const accessToken = await getAccessToken()
      const data = await apiCreateQRCode({ accessToken, path: `pages/auth/index?authId=${authId}&userId=${sessionId}` })
      JsonResult.success({
        req,
        response,
        message: '生成用户二维码成功',
        data: 'data:image/png;base64,' + data
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '生成用户二维码失败' })
    }
  }

  /**
   * miniweb user login
   * @param {*} req
   * @param {*} response
   */
  async miniWebUserLogin (req, response) {
    try {
      const { code, authId } = req.body
      const { openid, session_key: sessionKey } = await apiLoginMiniWeb(code)
      const user = await UserModel.findOne({ openid })
      if (user) {
        const accessToken = await this.loginResponseResult(req, response, user.id, '3')
        if (authId && accessToken) {
          (clients[authId] || []).forEach(client => {
            client.send(JSON.stringify({
              accessToken,
              messagepush: true
            }))
            client.close()
          })
          clients[authId] = null
        }
        return
      }
      const userId = await UserModel.create({ openid, sessionKey, avatar: 'https://image.zdxblog.cn/picture/16849805191921684980519192.png' })
      this.sendSystemRegisterMessage(userId)
      const newuser = await UserModel.findOne({ openid })
      const accessToken = await this.loginResponseResult(req, response, newuser.id, '3')
      if (authId && accessToken) {
        (clients[authId] || []).forEach(client => {
          client.send(JSON.stringify({
            accessToken,
            messagepush: true
          }))
          client.close()
        })
        clients[authId] = null
      }
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '微信小程序登录失败' })
    }
  }

  /**
   * user bind weixin
   * @param {*} req
   * @param {*} response
   * @returns
   */
  async userBindWeixin (req, response) {
    try {
      const { code, authId, userId } = req.body
      const { openid, session_key: sessionKey } = await apiLoginMiniWeb(code)
      const wxuser = await UserModel.findOne({ openid })
      if (wxuser) {
        return JsonResult.fail({ req, response, message: '此微信已绑定用户' })
      }
      const user = await UserModel.findOne({ id: userId })
      if (!user) {
        return JsonResult.fail({ req, response, message: '用户不存在' })
      }
      if (user.openid) {
        return JsonResult.fail({ req, response, message: '此用户已绑定微信' })
      }
      await UserModel.update({ id: userId, openid, sessionKey })
      const accessToken = jwt.sign({
        id: user.id,
        openid,
        sessionKey
      }, trash.jsonSecretkey, { expiresIn: trash.expiresIn })
      if (authId) {
        (clients[authId] || []).forEach(client => {
          client.send(JSON.stringify({
            accessToken,
            messagepush: true
          }))
          client.close()
        })
        clients[authId] = null
      }
      return JsonResult.success({
        req,
        response,
        data: { ...user, accessToken },
        message: '用户绑定微信成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '用户绑定微信失败' })
    }
  }

  /**
   * user bind username and password
   * @param {*} req
   * @param {*} response
   */
  async userBindUserName (req, response) {
    try {
      const { username, password, confirmPassword, code } = req.body
      const captcha = req.session.captcha
      if (!captcha || (code.toLowerCase() !== captcha.toLowerCase())) {
        return JsonResult.fail({ req, response, message: '验证码错误' })
      }
      const { aesKey, aesIv } = req.session.publicSecretKeys
      const userdepassword = decrypt(password, aesKey, aesIv)
      const userdeConfirmpassword = decrypt(confirmPassword, aesKey, aesIv)
      if (userdepassword !== userdeConfirmpassword) {
        return JsonResult.fail({ req, response, message: '两次密码不一致' })
      }
      const user = req.sessionuser
      if (user.username) {
        return JsonResult.fail({ req, response, message: '该用户已有用户名' })
      }
      await UserModel.bindUserName({
        id: user.id,
        username,
        password: encrypt(userdepassword, trash.aesKey, trash.aesIIv),
        bindPassword: 1
      })
      return JsonResult.success({
        req,
        response,
        data: { ...user, username, bindPassword: 1 },
        message: '用户绑定用户名密码成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '绑定用户名密码失败' })
    }
  }

  /**
   * user unbind email or weixin
   * @param {*} req
   * @param {*} response
   */
  async userUnBindRelation (req, response) {
    try {
      const type = req.query.type
      const user = req.sessionuser
      const { username, openid, email, giteeId, githubId } = user
      if ([username, openid, email, giteeId, githubId].filter(Boolean).length === 1) {
        return JsonResult.fail({ req, response, message: '唯一的登录方式，不能解绑' })
      }
      await UserModel.remove(user.id, type)
      const data = await UserModel.findOne({ id: user.id }, true)
      JsonResult.success({
        req,
        response,
        data,
        message: '解绑成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '解绑失败' })
    }
  }

  async userSignIn (req, response) {
    try {
      const userId = req.sessionuser.id
      const signTime = dateFormat(new Date(), 'YYYY-mm-dd')
      const exist = await signInModel.exist(userId, signTime)
      if (exist) {
        return JsonResult.fail({ req, response, message: '重复签到' })
      }
      await signInModel.add(userId, signTime)
      UserModel.updateExp(userId, 5)
      JsonResult.success({
        req,
        response,
        message: '签到成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '签到失败' })
    }
  }

  async getSignStatus (req, response) {
    const userId = req.sessionuser.id
    const signTime = dateFormat(new Date(), 'YYYY-mm-dd')
    try {
      const exist = await signInModel.exist(userId, signTime)
      JsonResult.success({
        req,
        response,
        data: Boolean(exist),
        message: 'success'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: '获取签到状态失败' })
    }
  }

  async authGiteeLogin (req, response) {
    try {
      const accessToken = await apiGetGiteeAccesstokenByCode(req.body.code)
      const userInfo = await apiGetGiteeUserInfo(accessToken)
      const { id, name, avatar_url: avatar } = userInfo
      const exist = await UserModel.findOne({ giteeId: id }, true)
      if (exist) {
        await this.loginResponseResult(req, response, exist.id, '4')
      } else {
        const userId = await UserModel.create({
          giteeId: id,
          giteeName: name,
          nickname: name,
          avatar
        })
        this.sendSystemRegisterMessage(userId)
        await this.loginResponseResult(req, response, userId, '4')
      }
    } catch (error) {
      JsonResult.fail({ req, response, error, message: 'gitee用户授权登录失败' })
    }
  }

  async authBindGitee (req, response) {
    try {
      const accessToken = await apiGetGiteeAccesstokenByCode(req.body.code)
      const userInfo = await apiGetGiteeUserInfo(accessToken)
      const { id: giteeId, name: giteeName } = userInfo
      const exist = await UserModel.findOne({ giteeId }, true)
      if (exist) {
        return JsonResult.fail({ req, response, message: `该gitee账号已被用户${exist.nickanme}绑定` })
      }
      const user = await UserModel.findOne({ id: req.sessionuser.id })
      await UserModel.update({ id: user.id, giteeId, giteeName })
      JsonResult.success({
        req,
        response,
        message: 'gitee绑定成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: 'gitee绑定失败' })
    }
  }

  async authGithubLogin (req, response) {
    try {
      const accessToken = await apiGetGithubAccesstokenByCode(req.body.code)
      const userInfo = await apiGetGithubUserInfo(accessToken)
      const { id, name, avatar_url: avatar } = userInfo
      const exist = await UserModel.findOne({ githubId: id }, true)
      if (exist) {
        await this.loginResponseResult(req, response, exist.id, '5')
      } else {
        const userId = await UserModel.create({
          githubId: id,
          githubName: name,
          nickname: name,
          avatar
        })
        this.sendSystemRegisterMessage(userId)
        await this.loginResponseResult(req, response, userId, '5')
      }
    } catch (error) {
      JsonResult.fail({ req, response, error, message: 'github用户授权登录失败' })
    }
  }

  async authBindGithub (req, response) {
    try {
      const accessToken = await apiGetGithubAccesstokenByCode(req.body.code)
      const userInfo = await apiGetGithubUserInfo(accessToken)
      const { id: githubId, name: githubName } = userInfo
      const exist = await UserModel.findOne({ githubId }, true)
      if (exist) {
        return JsonResult.fail({ req, response, message: `该github账号已被用户${exist.nickanme}绑定` })
      }
      const user = await UserModel.findOne({ id: req.sessionuser.id })
      await UserModel.update({ id: user.id, githubId, githubName })
      JsonResult.success({
        req,
        response,
        message: 'github绑定成功'
      })
    } catch (error) {
      JsonResult.fail({ req, response, error, message: 'github绑定失败' })
    }
  }

  async loginResponseResult (req, response, userId, loginType) {
    const ip = req.clientIp
    const user = await UserModel.findOne({ id: userId }, true)
    if (user.status === '0') {
      JsonResult.fail({
        req,
        response,
        message: '该用户已被禁用'
      })
      return null
    }
    const accessToken = jwt.sign({ id: user.id }, trash.jsonSecretkey, { expiresIn: trash.expiresIn })
    JsonResult.success({
      req,
      response,
      message: '用户登录成功',
      data: { ...user, accessToken }
    })
    apiGetIPAddress(ip).then(({ address }) => {
      LoginLogController.add({ userId: user.id, ip, address, loginType })
    })
    return accessToken
  }

  sendSystemRegisterMessage (userId) {
    const content = `<p>欢迎来到<span class="site-title blue_name">GimiShare</span>博客！因为有你的参与，我的存在才显得更具有意义~</p>
        <p>点击<a target="_blank" href="/writecenter">创作文章</a>即刻开始吧！</p>`
    MessageModel.add({ sourceUserId: '1', targetUserId: userId, itemType: '5', content })
  }
}

module.exports = new UserController()
